{
 "metadata": {
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.6"
  },
  "orig_nbformat": 4,
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3.9.6 64-bit ('DataAnalysis': venv)"
  },
  "interpreter": {
   "hash": "0fe90ac710750590bd916650f441b3b2233faa2658175d6302b22e90f225e38d"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2,
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# 4. NumPy基础：数组与向量计算  NumPy Basics: Arrays and Vectorized Computation"
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 4.1 NumPy中的ndarray：一种多维数组对象  The NumPy ndarray: A Multidimensional Array Object\r\n",
    "\r\n",
    "+ 针对整个ndarray的数学运算相当于针对其中每个元素作相同的数学运算\r\n",
    "+ ndarray中的元素必须为同种类型的数据\r\n",
    "+ ndarray的重要属性：\r\n",
    "    + `.dim`：Number of Array Dimension（一维数组、二维数组等）\r\n",
    "    + `.shape`Tuple of Array Dimension（每个维度的元组，类似矩阵的m行n列）\r\n",
    "    + `.dtype`：Data-type of Array's Elements\r\n",
    "+ array、NumPy array、ndarray 均指 ndarray object"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 模块导入\r\n",
    "import os, sys\r\n",
    "sys.path.append(os.path.dirname(os.getcwd()))\r\n",
    "import numpy\r\n",
    "from dependency import arr_info"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 4.1.1 创建数组 Creating ndarrays"
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "#### 1、创建任意ndarray\r\n",
    "\r\n",
    "+ `numpy.array(sequence_like)` ：函数，创建任意ndarray，参数为序列型的对象（包括其他数组）\r\n",
    "+ `numpy.asarray()`"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 1、利用numpy.array()函数创建任意ndarray\r\n",
    "\r\n",
    "data_1 = [6, 7.5, 8, 0, 1]\r\n",
    "array1_1 = numpy.array(data_1)\r\n",
    "\r\n",
    "data_2 = (9, 5, 2, 6, 7, 7, 1, 0)\r\n",
    "array1_2 = numpy.array(data_2)\r\n",
    "\r\n",
    "data_3 = [ [11,12,13,14], [21,22,23,24], [31,32,33,34] ]\r\n",
    "array1_3 = numpy.array(data_3)\r\n",
    "\r\n",
    "arr_info([array1_1, array1_2, array1_3])"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "#### 2、创建特定的ndarray\r\n",
    "\r\n",
    "利用特定的函数创建特定的ndarray\r\n",
    "\r\n",
    "类似 range()，创建一个ndarray类的序列\r\n",
    "+ `numpy.arange(n)`\r\n",
    "+ `numpy.arange(start=m, stop=n, step=p)`\r\n",
    "\r\n",
    "零矩阵\r\n",
    "+ `numpy.zeros((m,n))`\r\n",
    "+ `numpy.zeros_like(array)`\r\n",
    "\r\n",
    "全部元素为 1\r\n",
    "+ `numpy.ones((m,n))`\r\n",
    "+ `numpy.ones_like(array)`\r\n",
    "\r\n",
    "空矩阵\r\n",
    "+ `numpy.empty((m,n))`\r\n",
    "+ `numpy.empty_like(array)`\r\n",
    "\r\n",
    "单位矩阵（只能为方阵）\r\n",
    "+ `numpy.eye(n)`\r\n",
    "+ `numpy.identity(n)`"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 2、利用特定的函数创建特定的ndarray\r\n",
    "\r\n",
    "array1_4 = numpy.arange(10)\r\n",
    "\r\n",
    "array1_5 = numpy.zeros((3,4))\r\n",
    "array1_6 = numpy.zeros_like(array1_1)\r\n",
    "\r\n",
    "array1_7 = numpy.ones((3,4))\r\n",
    "array1_8 = numpy.ones_like(array1_2)\r\n",
    "\r\n",
    "array1_9 = numpy.eye(3)\r\n",
    "array1_10 = numpy.identity(3)\r\n",
    "\r\n",
    "array1_11 = numpy.empty((3,4))\r\n",
    "array1_12 = numpy.empty_like(array1_3)\r\n",
    "\r\n",
    "array_list = [array1_4, array1_5, array1_6, array1_7, array1_8, array1_9, array1_10, array1_11, array1_12]\r\n",
    "arr_info(array_list)"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "【注意】\r\n",
    "+ `numpy.arange()`、`numpy.eye()`、`numpy.identity` 只能传入一个数值，而非元组\r\n",
    "+ 其他函数传入元组时必须要加上小括号，否则会将第二个数字识别为下一个位置参数（即dtype参数），而加上括号则会将整个元组视为一个参数传入函数\r\n",
    "+ 如果不指定创建ndarray的数据类型（dtype），`numpy.array()`函数会判断传入的数据类型选择最佳类型，其他函数则默认创建为float64类型"
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 4.1.2 ndarray的数据类型 Data Type for ndarrays"
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "#### 数据类型概览\r\n",
    "\r\n",
    "+ 整型（有符号）：\r\n",
    "    + int8, \r\n",
    "    + int16, \r\n",
    "    + int32, \r\n",
    "    + int64\r\n",
    "+ 整型（无符号）：\r\n",
    "    + uint8, \r\n",
    "    + uint16, \r\n",
    "    + uint32, \r\n",
    "    + uint64\r\n",
    "+ 浮点型：\r\n",
    "    + float16, \r\n",
    "    + float32, \r\n",
    "    + float64, \r\n",
    "    + float128\r\n",
    "+ 复数：\r\n",
    "    + complex64, \r\n",
    "    + complex128, \r\n",
    "    + complex256\r\n",
    "+ 其他：\r\n",
    "    + bool, \r\n",
    "    + object, \r\n",
    "    + string_, \r\n",
    "    + unicode_"
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "#### 指定数据类型的方法\r\n",
    "\r\n",
    "1、在创建ndarray时在函数中利用关键词参数 `dtype=numpy.xxx` 显式指定\r\n",
    "\r\n",
    "2、调用ndarray的 `.astype(numpy.xxx)` 类方法进行转换"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 1、在创建ndarray时在函数中利用关键词参数 ctype=numpy.xxx 显式指定\r\n",
    "\r\n",
    "array2_1 = numpy.array([1, 2, 3, 4, 5], dtype=numpy.float64)\r\n",
    "array2_2 = numpy.array([1.2, 2.4, 3.5, 4.6, 5.9], dtype=numpy.int32)    # 丢失小数部分，且并非四舍五入，而是全部舍去\r\n",
    "empty_uint32 = numpy.empty((6), dtype=\"u4\")   # 使用dtype的缩写来指定（uint32的缩写为u4）\r\n",
    "\r\n",
    "arr_info([array2_1, array2_2, empty_uint32])"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 2、调用ndarray的 .astype(numpy.xxx) 类方法进行转换\r\n",
    "\r\n",
    "array2_3 = numpy.array([1, 2, 3, 4, 5])\r\n",
    "array2_4 = numpy.array([1.2, 2.4, 3.5, 4.6, 5.9])\r\n",
    "array2_5 = numpy.array([\"1.3\", \"2.5\", \"3.6\", \"4.8\", \"5.9\"])\r\n",
    "\r\n",
    "float_array_1 = array2_3.astype(numpy.float64)\r\n",
    "int_array = array2_4.astype(numpy.int32)     # 由浮点数转换为整数将会丢失小数部分，且并非四舍五入，而是全部舍去\r\n",
    "str_to_num_array = array2_5.astype(float)    # 即使将参数写为Python内建的float类型，NumPy也会自动识别为合适的NmuPy数据类型，例如此处将转换为numpy.float64\r\n",
    "float_array_2 = array2_3.astype(array2_4.dtype)\r\n",
    "\r\n",
    "arr_info([float_array_1, int_array, array2_5, str_to_num_array, float_array_2])"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 4.1.3 在array与标量之间的运算 Operations between Arrays and Scalars\r\n",
    "\r\n",
    "+ ndarray与标量的计算相当于每个元素与该标量计算。可以利用“向量化（Vectorization）”的数据完成批量的计算，不需要写循环；\r\n",
    "+ 两个ndarray之间也可以进行四则运算、乘方等数学运算。大小相等的数组之间的算数运算都将应用于元素的级别；\r\n",
    "+ 大小不同的数组间运算称之为广播（Broadcast），将在12章进行讨论。"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# array与标量之间的运算\r\n",
    "\r\n",
    "array3_1 = numpy.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])\r\n",
    "array3_2 = numpy.array([[2,1,3,4], [7,6,5,8], [12,11,9,10]])\r\n",
    "\r\n",
    "array_plus = array3_1 + array3_2        # 加法\r\n",
    "array_minus = array3_1 - array3_2       # 减法\r\n",
    "array_multiple = array3_1 * array3_2    # 乘法\r\n",
    "array_divide = array3_1 / array3_2      # 除法\r\n",
    "array_power = array3_1 ** 2             # 乘方\r\n",
    "array_mix = ((array3_1 + 3) / 2) ** 3   # 混合运算\r\n",
    "\r\n",
    "arr_info([array_plus, array_minus, array_multiple, array_divide, array_power, array_mix])"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 4.1.4 基本索引与切片 Basic Indexing and Slicing\r\n",
    "\r\n",
    "索引与轴的关系：\r\n",
    "\r\n",
    "+ ndarray轴的个数等于 `ndim`，即：ndarray是几维数组，就有几个 `axis`；\r\n",
    "+ 最外层的 `[ ]` 是 `axis=0`，次外层 `[ [] ]` 是 `axis=1`，以此类推；\r\n",
    "+ `axis` 的顺序与 `.shape` 元组中维度的顺序一致。例如：`shape=(2,3,4)`，则 `axis=0` 有2排元素，`axis=1` 有3排元素，`axis=2` 有4排元素；\r\n",
    "\r\n",
    "切片（slice）：取子集。\r\n",
    "\r\n",
    "+ NumPy的切片并不会将切片后的数据复制到一个新的数组，而是原数组的一个“视图”，这可以在处理大数据集的时候避免内存出错。如果需要复制数组，应当显式地调用类方法：`arr[s:t].copy()`\r\n",
    "+ 对切片的赋值将会覆盖整个选区"
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "#### 一维数组的索引与切片"
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "##### 索引与切片\r\n",
    "\r\n",
    "方式与Python列表相同。\r\n",
    "+ `array[ i ]`：引用一个元素（引用 index=i 的元素）\r\n",
    "+ `array[s:t]`：设定切片的起始索引（start）与结束索引（stop），切片的元素有 (t-s) 个。【注意，不包含结束索引的元素】\r\n",
    "+ `array[ : ]`：切片所有元素\r\n",
    "+ `array[s: ]`：设定切片从 起始索引（start） 到 最后一个索引，切片的元素有 (e+1-s) 个，e为末位元素（end）的索引。\r\n",
    "+ `array[ :t]`：设定切片从 第一个索引 到 结束索引（stop），切片的元素有 (t-0) 个。【注意，不包含结束索引的元素】"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 一维数组的索引与切片\r\n",
    "\r\n",
    "array4_1 = numpy.arange(10)\r\n",
    "\r\n",
    "arr_info([ array4_1 ])\r\n",
    "arr_info([ array4_1[ 5 ] ])       # 利用索引调用其中的一个元素\r\n",
    "arr_info([ array4_1[5:8] ])       # 利用起始索引和结束索引切片，注意：结束元素 index=7，而不是 8\r\n",
    "arr_info([ array4_1[5: ] ])       # 从起始索引到末尾元素切片：index=5 到 index=9 \r\n",
    "arr_info([ array4_1[ :8] ])       # 从首位元素到结束索引切片：index=0 到 index=7，注意不是 8"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "##### 负数索引与切片\r\n",
    "\r\n",
    "+ `array[  -1  ]`：引用最后一个元素\r\n",
    "+ `array[-t:-s]`：设定切片的起始索引（start）与结束索引（stop），切片的元素有 (t-s) 个。【注意，不包含起始索引 `[-s]` 的元素】\r\n",
    "+ `array[-t:  ]`：设定切片从 最后一个索引 到 结束索引（stop），切片的元素有 |-t-(-1)+1| = t 个。\r\n",
    "+ `array[  :-s]`：设定切片从 起始索引（start） 到 第一个索引，切片的元素有 (t-0) 个。【注意，不包含结束索引的元素】\r\n",
    "+ `array[-(e+1)]`：引用第一个元素"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 负数索引\r\n",
    "\r\n",
    "print(\"原数组:\", array4_1)\r\n",
    "\r\n",
    "arr_info([ array4_1[   -1] ])\r\n",
    "arr_info([ array4_1[-8:-5] ])\r\n",
    "arr_info([ array4_1[-8:  ] ])\r\n",
    "arr_info([ array4_1[  :-5] ])\r\n",
    "arr_info([ array4_1[-10  ] ])"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "##### 切片的赋值\r\n",
    "\r\n",
    "给切片数组赋值，每个元素都将被赋值为相同的值"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 切片的赋值\r\n",
    "\r\n",
    "array4_1[5:8] = 12      # 给切片数组赋值，每个元素都将被赋值为相同的值\r\n",
    "arr_info([array4_1])\r\n",
    "\r\n",
    "arr_slice = array4_1[5:8]\r\n",
    "\r\n",
    "arr_slice[1] = 36\r\n",
    "arr_info([arr_slice])\r\n",
    "\r\n",
    "arr_slice[:] = 99       # arr = 99 的语法是错误的，无法这样赋值\r\n",
    "arr_info([arr_slice])"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "#### 多维数组的索引"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 多维数组的索引\r\n",
    "\r\n",
    "#  二维数组\r\n",
    "array4_2 = numpy.array([ \r\n",
    "        [1,2,3,4], \r\n",
    "        [5,6,7,8], \r\n",
    "        [9,10,11,12] \r\n",
    "    ])\r\n",
    "\r\n",
    "# 三维数组\r\n",
    "array4_3 = numpy.array([ \r\n",
    "        [ \r\n",
    "            [1,2,3,4], \r\n",
    "            [5,6,7,8], \r\n",
    "            [9,10,11,12] \r\n",
    "        ],  \r\n",
    "        [ \r\n",
    "            [13,14,15,16], \r\n",
    "            [17,18,19,20], \r\n",
    "            [21,22,23,24] \r\n",
    "        ] \r\n",
    "    ])  \r\n",
    "arr_info([array4_2, array4_3])\r\n",
    "\r\n",
    "# 两种等价的方式访问单个元素\r\n",
    "arr_info([ array4_2[0][1], array4_2[0, 1] ])\r\n",
    "arr_info([ array4_3[0][1][2], array4_3[0, 1, 2] ])\r\n",
    "\r\n",
    "# n维数组的一阶索引是(n-1)维数组\r\n",
    "arr_info([ array4_2[0], array4_3[0] ])\r\n",
    "\r\n",
    "# n维数组的二阶索引是(n-2)维数组，三维数组的[i][j]索引将获得一个一维数组\r\n",
    "arr_info([ array4_3[0][1] ])\r\n",
    "\r\n",
    "# 【注】\r\n",
    "# 这里的阶数是自己定义的名称，并非标准名称。阶数指索引的维度：一阶即[i]，二阶即[i][j]；\r\n",
    "# 其实可以很直观地理解：三维切一刀将会得到二维，三维切三刀会得到零维（元素）；二维切一刀得到一维，等。即索引n次，维度变为(ndim-n）；\r\n",
    "# n维切n刀：访问单个元素（元素可以类比为点，而点是零维的）"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 多维数组的赋值\r\n",
    "\r\n",
    "origin_array_1 = array4_3[0]            # origin_array 是一个二维（3x4）数组\r\n",
    "origin_array_2 = array4_3[0].copy()     # 只有调用 .copy() 方法，数组的值才会被真正意义上被复制\r\n",
    "array4_3[0] = 37\r\n",
    "\r\n",
    "arr_info([array4_3])\r\n",
    "\r\n",
    "array4_3[0] = origin_array_1            # 映射原数组的地址，原数组的值改变后，切片数组的值也会改变\r\n",
    "arr_info([origin_array_1, array4_3 ])\r\n",
    "\r\n",
    "array4_3[0] = origin_array_2            # 原数组改变后，切片数组的值不会改变，仍然保持赋值时的值\r\n",
    "arr_info([origin_array_2, array4_3])"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "#### 多维数组的切片\r\n",
    "\r\n",
    "+ `array[s:][:t]`：多次切片。先切一刀，再切一刀。相当于 `array[s:]` 这个数组做了 `[:t]` 的切片：`(array[s:])[:t]`。每次取的是低一维度的索引（第二个参数不是列）\r\n",
    "+ `array[s:, :t]`：多维切片。两刀一起切，第一个参数是行，第二个参数是列（类似于取交集、二元不等式的图解区域）\r\n",
    "\r\n",
    "【注意】\r\n",
    "+ 这里“多次切片”、“多维切片”是自己定义的名称，并非标准表述；\r\n",
    "+ 切片与访问元素不同，`array[s:,:t]` 与 `array[s:][:t]` 不等价！！！"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 二维数组的切片\r\n",
    "\r\n",
    "arr_info([ array4_2 ])\r\n",
    "arr_info([ array4_2[ :2] ])     # 切片不改变数组的维数（ndim）\r\n",
    "arr_info([ array4_2[ :2][1: ], array4_2[ :2, 1: ] ])  # 注意：切片与访问元素不同，array4_2[:2,1:] 与 array4_2[:2][1:] 不等价！！！\r\n",
    "arr_info([ array4_2[ : ][ :1], array4_2[ : ,  :1] ])"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 取顺序主子式（注意：数学意义上的顺序主子式仅针对方阵）\r\n",
    "\r\n",
    "array4_4 = numpy.array([ [1,2,3], [4,5,6], [7,8,9]] )\r\n",
    "\r\n",
    "arr_info([ array4_4[ :1, :1 ] ])\r\n",
    "arr_info([ array4_4[ :2, :2 ] ])\r\n",
    "arr_info([ array4_4[ :3, :3 ] ])"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 取某一行、某一列\r\n",
    "\r\n",
    "arr_info([ array4_2 ])\r\n",
    "arr_info([ array4_2[ : ,  :1] ])     # 取第 0 列\r\n",
    "arr_info([ array4_2[ :2][1: ], array4_2[1] ])    # 取第 1 行\r\n",
    "# 多次切片取一行的维数（ndim）仍为 2，利用索引取第一行的维数（ndim）则为 1"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 三维数组\r\n",
    "\r\n",
    "array4_5 = numpy.array([ \r\n",
    "        [ [ 1, 2 ,3 ,4 ,5], [ 6, 7, 8, 9,10], [11,12,13,14,15], [16,17,18,19,20] ], \r\n",
    "        [ [21,22,23,24,25], [26,27,28,29,30], [31,32,33,34,35], [36,37,38,39,40] ], \r\n",
    "        [ [41,42,43,44,45], [46,47,48,49,50], [51,52,53,54,55], [56,57,58,59,60] ] \r\n",
    "    ])\r\n",
    "\r\n",
    "arr_info([ array4_5, array4_5[:1], array4_5[:1][:2] ])  # 多次切片时，实际上下一次切的还是三维数组\r\n",
    "arr_info([ array4_5[:1][:2][:3], array4_5[ :1, :2, :3] ])\r\n",
    "arr_info([ array4_5[:1, :2][:3], array4_5[ :1][:2, :3] ])"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "#### 带有切片的索引 Indexing with slices\r\n",
    "\r\n",
    "对于多维数组，可以在每个维度进行切片（ndim不变）。也可以结合索引变为低维度的数组，即带有切片的索引（ndim降低）。\r\n",
    "\r\n",
    "+ `[a, :b]` 的情况可以先选中 a 行的数据，然后看 :b 切了哪一部分\r\n",
    "+ `[:a, b]` 的情况可以先选中 b 列的数据，然后看 :a 切了哪一部分"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 带有切片的索引\r\n",
    "\r\n",
    "arr_info([ array4_2 ])\r\n",
    "arr_info([ array4_2[2][ :1], array4_2[ 2, :1] ])    # 先选中第2行\r\n",
    "arr_info([ array4_2[ :2][1], array4_2[:2,  1] ])    # 先选中第1列"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 4.1.5 布尔索引 Boolean Indexing\r\n",
    "\r\n",
    "+ 布尔数组的轴长度必须与被索引轴的长度一致\r\n",
    "+ Python关键词 `and`、`or` 在布尔索引中无效"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 布尔索引\r\n",
    "\r\n",
    "names = numpy.array([\"Kiana\", \"Kiana\", \"Kaslana\", \"Kiana Kaslana\", \"Kiana\", \"Raiden Mei\", \"Bronya Zaychik\" ])\r\n",
    "data = numpy.random.randn(7, 7)\r\n",
    "\r\n",
    "arr_info([ names, data ])\r\n",
    "arr_info([ names==\"Kiana\" ])            # 得到布尔数组\r\n",
    "\r\n",
    "arr_info([ data[names==\"Kiana\"] ])      # 对 axis0 进行布尔索引"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 反向选择（非、不等于）\r\n",
    "\r\n",
    "arr_info([ data[names != \"Kiana\"] ])"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 多个逻辑条件（Python关键词and、or在布尔索引中无效）\r\n",
    "\r\n",
    "select = ((names==\"Kiana\") | (names==\"Raiden Mei\")) & (names!=\"Kaslana\")\r\n",
    "\r\n",
    "arr_info([ names[select], data[select] ])"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 布尔索引与普通索引、切片的结合\r\n",
    "\r\n",
    "arr_info([ data[names==\"Kiana\"][1] ])   # 对 axis0 进行布尔索引（行），并对所得数组取 index=1 的元素\r\n",
    "arr_info([ data[1][names==\"Kiana\"] ])   # 取 index=1的元素，并对其进行布尔索引，相当于对 data[1] 的 axis1进行布尔索引（列）\r\n",
    "\r\n",
    "arr_info([ data[names==\"Kiana\"][1:] ])   # \r\n",
    "# arr_info([ data[1:][names==\"Kiana\"] ])   # 如果先切片，则索引不匹配，不能进行布尔索引"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 通过布尔索引更方便地选择数据并赋值\r\n",
    "\r\n",
    "data[data < 0] = 0\r\n",
    "arr_info([data])\r\n",
    "\r\n",
    "data[names==\"Kiana\"] = 520\r\n",
    "arr_info([data])"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 4.1.6 花式索引 Fancy Indexing\r\n",
    "\r\n",
    "在数组索引的位置传入一个包含索引的列表（或数组）：\r\n",
    "\r\n",
    "```python\r\n",
    "index_list = [0, 2, 4, 6]   # 引用 index = 0、2、4、6 的元素\r\n",
    "array[ index_list ]\r\n",
    "```\r\n",
    "\r\n",
    "【注意】花式索引与切片不同，其总是将数据复制到新数组中"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 花式索引\r\n",
    "\r\n",
    "array6_1 = numpy.array([1,2,3,4,5,6,7,8,9,10])\r\n",
    "array6_2 = numpy.empty((8, 4))\r\n",
    "for i in range(8):\r\n",
    "    array6_1[i] = i + 1\r\n",
    "\r\n",
    "# 一维数组\r\n",
    "arr_info([ array6_1 ])\r\n",
    "arr_info([ array6_1[ [4,3,0,6] ] ])\r\n",
    "arr_info([ array6_1[ [-4,-3,-1,-6] ] ])\r\n",
    "\r\n",
    "# 二维数组\r\n",
    "arr_info([ array6_2 ])\r\n",
    "arr_info([ array6_2[ [4,3,0,6] ] ])\r\n",
    "arr_info([ array6_2[ [-4,-3,-1,-6] ] ]) # 花式索引依然支持负数索引"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 传入多组索引\r\n",
    "\r\n",
    "array6_3 = numpy.arange(32).reshape((8,4))\r\n",
    "\r\n",
    "arr_info([ array6_3 ])\r\n",
    "arr_info([ array6_3[ [1,5,7,2], [0,3,1,2] ]])   # 选中index为1、5、7、2的行，以及0、3、1、2列，即：[1,0], [5,3], [7,1], [2,2] 元素\r\n",
    "arr_info([ array6_3[ [1,5,7,2] ]  [ :,[0,3,1,2] ] ])\r\n",
    "arr_info([ array6_3[ numpy.ix_([1,5,7,2], [0,3,1,2])] ])"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 4.1.7 数组的转置与轴的交换 Transposing Arrays and Swapping Axes\r\n",
    "\r\n",
    "+ `array.T`：属性\r\n",
    "+ `array.transpose((a,b,c))`：函数，参数为转置后轴索引的元组，ndarray轴索引有几个，参数的元组也应该写几个轴索引\r\n",
    "+ `array.swapaxes(a,b)`：函数，参数为需要转置的轴索引，只能交换两个轴，索引顺序与结果无关"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 数组的转置与轴的交换\r\n",
    "\r\n",
    "array7_1 = numpy.array([ [1,2,3,4], [5,6,7,8], [9,10,11,12] ])\r\n",
    "array7_2 = numpy.array([ [ [1,2,3,4], [5,6,7,8], [9,10,11,12] ], [ [13,14,15,16], [17,18,19,20], [21,22,23,24] ] ])\r\n",
    "\r\n",
    "arr_info([ array7_1, array7_1.T, array7_1.transpose((1,0)), array7_1.swapaxes(0,1) ])\r\n",
    "arr_info([ array7_2, array7_2.T, array7_2.transpose((2,1,0)), array7_1.swapaxes(0,1) ])\r\n",
    "# 三维.T属性相当于.transpose((2,1,0))，即三个轴都转置"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 计算矩阵内积\r\n",
    "\r\n",
    "inner_product = numpy.dot(array7_1, array7_1.T)\r\n",
    "\r\n",
    "arr_info([ inner_product ])"
   ],
   "outputs": [],
   "metadata": {}
  }
 ]
}